package comlib.arbiter

import chisel3._
import chisel3.util._

class SelectCell( priNum:Int, 
  select:(Vec[Bool],Vec[Bool],Vec[Bool],Vec[Bool],Vec[Bool])=>(Vec[Bool],Vec[Bool],Vec[Bool],Vec[Bool],Vec[Bool])
) extends MultiIOModule {

  /** 输入请求. true.B表示有请求。第i个元素代表优先级i的请求。 默认序号越小对应的优先级越高
    * @group IO */
  val r = IO(Input(Vec(priNum, Bool())))

  /** 东向输入屏蔽. true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
    * @group IO */
  val ei = IO(Input(Vec(priNum, Bool())))

  /** 东向输出屏蔽. true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
    * @group IO */
  val eo = IO(Output(Vec(priNum, Bool())))

  /** 南向输入屏蔽. true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
    * @group IO */
  val si = IO(Input(Vec(priNum, Bool())))

  /** 南向输出屏蔽. true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
    * @group IO */
  val so = IO(Output(Vec(priNum, Bool())))

  /** 西向输入屏蔽. true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
    * @group IO */
  val wi = IO(Input(Vec(priNum, Bool())))

  /** 西向输出屏蔽. true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
    * @group IO */
  val wo = IO(Output(Vec(priNum, Bool())))

  /** 北向输入屏蔽. true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
    * @group IO */
  val ni = IO(Input(Vec(priNum, Bool())))

  /** 北向输出屏蔽. true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
    * @group IO */
  val no = IO(Output(Vec(priNum, Bool())))

  /** 应答. true.B表示有应答。第i个元素代表优先级i的应答。默认序号越小对应的优先级越高
    * @group IO */
  val g = IO(Output(Vec(priNum, Bool())))

  protected val res = select( r, ei, si, wi, ni )

  g := res._1 
  eo := res._2 
  so := res._3 
  wo := res._4 
  no := res._5
}

/** SelectCell选择行为定义接口
 *
 * 用户通过扩展该接口定义来指定SelectCell的选择行为
 */
trait SelectAction{
  /** Grant定义接口 
   * @param x Mesh格子的行坐标
   * @param y Mesh格子的纵坐标
   * @param r 各优先级请求
   * @param ei 各优先级东向屏蔽标识
   * @param si 各优先级南向屏蔽标识
   * @param wi 各优先级西向屏蔽标识
   * @param ni 各优先级北向屏蔽标识
   * @return 5个元素组成的一个Tuple
   *   - g: 各优先级的Grant信息
   *   - eo: 各优先级的东向屏蔽标识
   *   - so: 各优先级的南向屏蔽标识
   *   - wo: 各优先级的西向屏蔽标识
   *   - no: 各优先级的北向屏蔽标识
   */
  def select(x:Int, y:Int)(r:Vec[Bool], ei:Vec[Bool], si:Vec[Bool], wi:Vec[Bool], ni:Vec[Bool]):(Vec[Bool], Vec[Bool], Vec[Bool], Vec[Bool], Vec[Bool])

  
}

trait SelectActionPriIndep extends SelectAction{

  /** Grant定义接口 
   * @param x Mesh格子的行坐标
   * @param y Mesh格子的纵坐标
   * @param r 请求
   * @param wi 西向屏蔽标识
   * @param ni 北向屏蔽标识
   * @return 3个元素组成的一个Tuple
   *   - g: 各优先级的Grant信息
   *   - eo: 东向屏蔽标识
   *   - so: 南向屏蔽标识
   */
  def selectIndep(x:Int, y:Int)(r:Bool, wi:Bool, ni:Bool) : (Bool, Bool, Bool)

  def select(x:Int, y:Int)(r:Vec[Bool], ei:Vec[Bool], si:Vec[Bool], wi:Vec[Bool], ni:Vec[Bool]):(Vec[Bool], Vec[Bool], Vec[Bool], Vec[Bool], Vec[Bool]) = {
    val res = (0 until r.size).map{ case p => selectIndep(x,y)(r(p), wi(p), ni(p)) }
    val g = VecInit(res.map(_._1))
    val eo = VecInit(res.map(_._2))
    val so = VecInit(res.map(_._3))
    (g, eo, so, ei, si)
  }

}

/** 寻找第一个"1"的Action */
class SelectFind1 extends SelectAction{
  def select(x:Int, y:Int)(r:Vec[Bool], ei:Vec[Bool], si:Vec[Bool], wi:Vec[Bool], ni:Vec[Bool]):(Vec[Bool], Vec[Bool], Vec[Bool], Vec[Bool], Vec[Bool]) = {
    val priNum = r.size
    val res = ( 0 until priNum ).map { case p =>
      val gi = r(p) & !(wi.slice(p, priNum).reduce(_|_)) & !(ni.slice(p,priNum).reduce(_|_))
      val g = if( p == priNum - 1 ) gi else gi & !(ei.slice(p+1,priNum).reduce(_|_)) & !(si.slice(p+1,priNum).reduce(_|_))
      val eo = g | wi(p)  // 东侧屏蔽条件：当前无仲裁，或者西侧对应优先级有屏蔽
      val so = g | ni(p)  // 南侧屏蔽条件：当前无仲裁，或者西侧对应优先级有屏蔽
      val wo = g | ei(p)  // 西侧屏蔽条件：当前无仲裁，或者东侧对应优先级有屏蔽
      val no = g | si(p)  // 北侧屏蔽条件：当前无仲裁，或者东侧对应优先级有屏蔽
      (g, eo, so, wo, no)
    }
    ( VecInit(res.map(_._1)), VecInit(res.map(_._2)), VecInit(res.map(_._3)), VecInit(res.map(_._4)), VecInit(res.map(_._5)) )
  }
}

/** 打平各个SelectCell后的选择行为 */
trait SelectParrel extends SelectActionPriIndep{
  def selectIndep(x:Int, y:Int)(r:Bool, wi:Bool, ni:Bool) = (false.B, false.B, false.B)
  def so(x:Int, y:Int, req:Vec[Vec[Bool]], cmi:Vec[Bool], rmi:Vec[Bool]):Bool
  /** East-Output 
   * @param x 选择SelectUnit在X轴(column)的位置
   * @param y 选择SelectUnit在Y轴(row)的位置
   * @param req 当前优先级请求矩阵
   * @param cmi 当前优先级cmi向量
   * @param rmi 当前优先级rmi向量
   * @return eo选择结果
   */
  def eo(x:Int, y:Int, req:Vec[Vec[Bool]], cmi:Vec[Bool], rmi:Vec[Bool]):Bool
  /** Grant
   * @param x 选择SelectUnit在X轴(column)的位置
   * @param y 选择SelectUnit在Y轴(row)的位置
   * @param req 当前优先级请求矩阵
   * @param cmi 当前优先级cmi向量
   * @param rmi 当前优先级rmi向量
   * @return grant选择结果
   */
  def g(x:Int, y:Int, req:Vec[Vec[Bool]], cmi:Vec[Bool], rmi:Vec[Bool]):Bool
}

class Select1Parrel extends SelectParrel{

  def so(x:Int, y:Int, req:Vec[Vec[Bool]], cmi:Vec[Bool], rmi:Vec[Bool]):Bool = {
    val n = if(y==0) cmi(x) else so(x, y-1, req, cmi, rmi)
    val w = if(x==0) rmi(y) else eo(x-1, y, req, cmi, rmi)
    n | (req(y)(x) & !w)
  }
  def eo(x:Int, y:Int, req:Vec[Vec[Bool]], cmi:Vec[Bool], rmi:Vec[Bool]):Bool = {
    val n = if(y==0) cmi(x) else so(x, y-1, req, cmi, rmi)
    val w = if(x==0) rmi(y) else eo(x-1, y, req, cmi, rmi)
    w | (req(y)(x) & !n)
  }
  def g(x:Int, y:Int, req:Vec[Vec[Bool]], cmi:Vec[Bool], rmi:Vec[Bool]):Bool = {
    val n = if( y==0 ) cmi(x) else so(x, y-1, req, cmi, rmi)
    val w = if( x==0 ) rmi(y) else eo(x-1, y, req, cmi, rmi)
    req(y)(x) & !n & !w
  }

}

class SelectUntil0 extends SelectActionPriIndep{
  def selectIndep(x:Int, y:Int)(r:Bool, wi:Bool, ni:Bool) : (Bool, Bool, Bool) = ( r & !wi & !ni, !r & !ni | wi, r & !wi | ni )
}


class ArbiterIO(ncol:Int, nrow:Int, npri:Int, ncp:Int, nrp:Int) extends Bundle {

  /** 输入行屏蔽信号。bit[i][j]代表对第i个优先级的第j行是否屏蔽；true.B表示需要屏蔽
    * @note 建议使用rmsk函数进行赋值
    * @group Input */
  val rmi = Input( Vec(npri, Vec(nrow, Bool())) )

  /** 输入列屏蔽信号。bit[i][j]代表对第i个优先级的第j列是否屏蔽；true.B表示需要屏蔽
    * @note 建议使用cmsk函数进行赋值
    * @group Input */
  val cmi = Input( Vec(npri, Vec(ncol, Bool())) )

  /** 请求信号。bit[i][j][k]表示对优先级i、行j、列k的请求;  true.B表示有请求 
    * @note 建议使用req函数进行赋值
    * @group Input */
  val req = Input( Vec(npri, Vec(nrow, Vec(ncol, Bool()))) )

  val cptr = Input( Vec(if(ncp>0) npri else 0, Vec(if(ncp>0) ncp else 0, Bool())) )
  val rptr = Input( Vec(if(nrp>0) npri else 0, Vec(if(nrp>0) nrp else 0, Bool())) )

  /** 输出行屏蔽信号。bit[i][j]代表对第i个优先级的第j行是否屏蔽；true.B表示需要屏蔽
    * @group Input */
  val rmo = Output( Vec(npri, Vec(nrow, Bool())) )
  val rmo0 = Output( Vec(nrow, Bool()) )

  /** 输出列屏蔽信号。bit[i][j]代表对第i个优先级的第j列是否屏蔽；true.B表示需要屏蔽
    * @group Input */
  val cmo = Output( Vec(npri, Vec(ncol, Bool())) )
  val cmo0 = Output( Vec(ncol, Bool()) )

  /** 行应答信号。bit[i][j][k]表示对优先级i、行j、列k的仲裁;  true.B表示有Grant 
    * @group Output */
  val gr = Output( Vec(npri, Vec(nrow, Vec(ncol, Bool()))) )
  val gr0 = Output( Vec(nrow, Vec(ncol, Bool())) )

  /** 行应答有效标识。bit[i][j]表示对优先级i、行j是否有Grant;  true.B表示有Grant 
    * @group Output */
  val grv = Output( Vec(npri, Vec(nrow, Bool())) )
  val grv0 = Output( Vec(nrow, Bool()) )

  /** 列应答信号。bit[i][j][k]表示对优先级i、列j、行k的仲裁;  true.B表示有Grant 
    * @group Output */
  val gc = Output( Vec(npri, Vec(ncol, Vec(nrow, Bool()))) )
  val gc0 = Output( Vec(ncol, Vec(nrow, Bool())) )

  /** 列应答有效标识。bit[i][j]表示对优先级i、列j是否有Grant;  true.B表示有Grant 
    * @group Output */
  val gcv = Output( Vec(npri, Vec(ncol, Bool())) )
  val gcv0 = Output( Vec(ncol, Bool()) )

  /** 行应答信号。bit[k]表示对优先级0、行0、列k的仲裁;  true.B表示有Grant 
    * @group Output */
  val gnt = Output( Vec(ncol, Bool()) )

  /** 行应答有效标识。表示对优先级0、行0是否有Grant;  true.B表示有Grant 
    * @group Output */
  val gv = Output( Bool() )

  override def cloneType = new ArbiterIO(ncol, nrow, npri, ncp, nrp).asInstanceOf[this.type]
}


/** 对仲裁算法模块的通用行为进行封装。
 *
 * 封装内容包括:
 *   - 基本接口
 *   - 基本函数
 *   - 接口间通用转化
 * @param ncol 仲裁器列个数
 * @param nrow 仲裁器行个数
 * @param npri 仲裁器优先级个数
 * @param ncp 仲裁器列指针位宽
 * @param nrp 仲裁器行指针位宽
 *
 * @note 输出接口中`xxx0`表示仅优先级0的输出信号。用于简化输出链接
 */
abstract class ArbiterCommon(val ncol:Int, val nrow:Int, val npri:Int, ncp:Int=0, nrp:Int=0) extends Module{

  /** @group IO */
  val io = IO(new ArbiterIO(ncol, nrow, npri, ncp, nrp))

  io.gc := VecInit((0 until npri).map{ case p =>
    VecInit((0 until ncol).map{ case c =>
      VecInit((0 until nrow).map{ case r => io.gr(p)(r)(c) })
    })
  })
  io.gr0 := io.gr(0)
  io.gc0 := io.gc(0)
  io.grv := VecInit(io.rmi.zip(io.rmo).map { case (i,o) => VecInit(i.zip(o).map { case (i1, o1) => ~i1 & o1 } )})
  io.gcv := VecInit(io.cmi.zip(io.cmo).map { case (i,o) => VecInit(i.zip(o).map { case (i1, o1) => ~i1 & o1 } )})
  io.grv0 := io.grv(0)
  io.gcv0 := io.gcv(0) 
  io.gnt := io.gr0(0)
  io.gv := io.req(0)(0).zip(io.cmi(0)).map{ case (r,m) => r & !m }.reduce(_|_)
  io.rmo0 := io.rmo(0)
  io.cmo0 := io.cmo(0)

  /** 扩展Bool值`r`到仲裁器的整个请求矩阵(即把r复制到所有优先级、所有行和所有列)
   * @param r 是否有请求标识
   * @group Input
   */
  def req(r:Bool):Vec[Vec[Vec[Bool]]] = 
    VecInit((0 until npri).map{ case _ =>
      VecInit((0 until nrow).map{ case _ => 
        VecInit((0 until ncol).map{ case _ => r })
      })
    })
  /** 扩展UInt值`r`到仲裁器的整个请求矩阵
   * @param r 是否有请求标识
   * 先把r转化为Bools，然后复制到所有优先级的所有行
   * @group Input
   */  
  def req(r:UInt):Vec[Vec[Vec[Bool]]] = 
    VecInit((0 until npri).map{ case _ =>
      VecInit((0 until nrow).map{ case _ => VecInit(r.asBools) })
    })
  /** 扩展Bool值`r`到仲裁器的整个请求矩阵
   * @param r 是否有请求标识
   * 把r复制到所有优先级的所有行
   * @group Input
   */  
  def req(r:Vec[Bool]):Vec[Vec[Vec[Bool]]] = 
    VecInit((0 until npri).map{ case _ =>
      VecInit((0 until nrow).map{ case _ => r })
    })
  /** 扩展Bool值`r`到仲裁器的整个请求矩阵
   * @param r 是否有请求标识
   * 把r复制到所有优先级
   * @group Input
   */  
  def req(r: => Vec[Vec[Bool]]):Vec[Vec[Vec[Bool]]] = VecInit((0 until npri).map{ case _ => r })
  /** 扩展Bool值`r`到仲裁器的整个请求矩阵
   * @param r 是否有请求标识
   * 把r中每一个UInt转化为Bools，然后复制到所有优先级
   * @group Input
   */
  def req(r:Vec[UInt], isUInt:Boolean):Vec[Vec[Vec[Bool]]] = VecInit((0 until npri).map{ case _ =>
    VecInit(r.map{ case rr => VecInit(rr.asBools) })
  })

  /**
   * @group Input
   */
  def rmi(m:Bool):Vec[Vec[Bool]] = VecInit(Seq.fill(npri)(VecInit((0 until nrow).map{ case _ => m }) ))
  /**
   * @group Input
   */
  def rmi(m:UInt):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => VecInit(m.asBools) })
  /**
   * @group Input
   */
  def rmi(m:Vec[Bool]):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => m })
  /**
   * @group Input
   */
  def rmi(m:Vec[UInt], isUInt:Boolean):Vec[Vec[Bool]] = VecInit(m.map{ case pm => VecInit(pm.asBools) })

  /**
   * @group Input
   */
  def cmi(m:Bool):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => VecInit((0 until ncol).map{ case _ => m }) })
  /**
   * @group Input
   */
  def cmi(m:UInt):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => VecInit(m.asBools) })
  /**
   * @group Input
   */
  def cmi(m:Vec[Bool]):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => m })
  /**
   * @group Input
   */
  def cmi(m:Vec[UInt], isUInt:Boolean):Vec[Vec[Bool]] = VecInit(m.map{ case pm => VecInit(pm.asBools) })
  
  /**
   * @group Input
   */
  def rptr(m:UInt):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => VecInit(m.asBools) })
  /**
   * @group Input
   */
  def rptr(m:Vec[Bool]):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => m })
  /**
   * @group Input
   */
  def rptr(m:Vec[UInt], isUInt:Boolean):Vec[Vec[Bool]] = VecInit(m.map{ case pm => VecInit(pm.asBools) })
  
  /**
   * @group Input
   */
  def cptr(m:UInt):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => VecInit(m.asBools) })
  /**
   * @group Input
   */
  def cptr(m:Vec[Bool]):Vec[Vec[Bool]] = VecInit((0 until npri).map{ case _ => m })
  /**
   * @group Input
   */
  def cptr(m:Vec[UInt], isUInt:Boolean):Vec[Vec[Bool]] = VecInit(m.map{ case pm => VecInit(pm.asBools) })

  /**
   * @group Output
   */
  def apply[D<:Data](in:Vec[D]):D=comlib.util.muxO(in, io.gnt)
  /**
   * @group Output
   */
  def apply[D<:Data](in:Vec[D], dft:D):D=Mux(io.gv, comlib.util.muxO(in, io.gnt), dft)
  
  /** 用于select函数的返回值封装
   * @param vld 选择结果有效标识
   * @param data 选择结果数据
   */
  case class MuxSel[D<:Data](vld:Bool, data:D)
  /** 按照当前仲裁器的仲裁结果选择数据
   *
   * 该函数仅使用Arbiter.io.gnt信号进行选择。所以仅适合一维仲裁器
   * @param in 输入数据
   * @return 返回选择结果
   * @group Output
   */
  def select[D<:Data](in:Vec[D]):MuxSel[D]={
    val res = comlib.util.muxO(in, io.gnt)
    MuxSel(io.gv, res)
  }
  /** 按照当前仲裁器的仲裁结果选择数据
   *
   * 该函数仅使用Arbiter.io.gnt信号进行选择。所以仅适合一维仲裁器
   * @param in 输入数据
   * @param dft 若仲裁无结果，则输出的默认数据
   * @return 返回选择结果
   * @group Output
   */
  def select[D<:Data](in:Vec[D], dft:D):MuxSel[D]={
    val res = Mux(io.gv, comlib.util.muxO(in, io.gnt), dft)
    MuxSel(io.gv, res)
  }

  case class XbSel[D<:Data](vlds:Vec[Bool], datas:Vec[D])
  /**
   * @group Output
   */
  def xb[D<:Data](in:Vec[D]):XbSel[D]={
    val res = io.gc0.map{ case gnt =>
      comlib.util.muxO(in, gnt)
    }
    XbSel(io.gcv0, VecInit(res))
  }
}

/** 选择单元Mesh网络
 * @param ncol 列数(>=1)
 * @param nrow 行数(>=1)
 * @param npri 优先级个数(>=1)
 */
class SelectMesh(ncol:Int=1, nrow:Int=1, npri:Int=1)(implicit val action:SelectAction=new SelectFind1) extends ArbiterCommon(ncol, nrow, npri){

  protected val rmsk = Wire(Vec(npri, Vec(nrow, Bool())))
  protected val cmsk = Wire(Vec(npri, Vec(ncol, Bool())))
  protected val grow = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))

  var action_i = action

  if( npri==1 && nrow==1 && action_i.isInstanceOf[SelectFind1] ){
    val realReq = VecInit(io.req(0)(0).zip(io.cmi(0)).map{ case (r, m) => r & !(m) })
    val gnt = sp( realReq )
    grow(0)(0) := Mux( io.rmi(0)(0), VecInit(Seq.fill(ncol)(false.B)), gnt )
    rmsk(0)(0) := io.rmi(0)(0) | realReq.reduce(_|_)
    cmsk(0) := VecInit( gnt.zip(io.cmi(0)).map{ case (g,m) => g|m } )
  }else if( action_i.isInstanceOf[SelectParrel]){

    val act = action_i.asInstanceOf[SelectParrel]
    for( p <- 0 until npri) {
      for( r <- 0 until nrow; c <- 0 until ncol ){
        grow(p)(r)(c) := act.g(c, r, io.req(p), io.cmi(p), io.rmi(p) )
      } 
      for( r <- 0 until nrow ){
        rmsk(p)(r) := act.eo(ncol-1, r, io.req(p), io.cmi(p), io.rmi(p) )
      }
      for( c <- 0 until ncol ){
        cmsk(p)(c) := act.so(c, nrow-1, io.req(p), io.cmi(p), io.rmi(p) )
      }
    }

  }else{
    val units = for( x <- 0 until nrow ) yield for( y <- 0 until ncol ) yield Module(new SelectCell( npri, action_i.select(x,y) ))

    for( r <- 0 until nrow; c <- 0 until ncol; p <- 0 until npri ){

      if( r==0 ) units(r)(c).ni(p) := io.cmi(p)(c)
      else units(r)(c).ni(p) := units(r-1)(c).so(p)

      if( c==0 ) units(r)(c).wi(p) := io.rmi(p)(r)
      else units(r)(c).wi(p) := units(r)(c-1).eo(p)

      if( r==nrow-1 ) units(r)(c).si(p) := false.B 
      else units(r)(c).si(p) := units(r+1)(c).no(p)

      if( c==ncol-1 ) units(r)(c).ei(p) := false.B 
      else units(r)(c).ei(p) := units(r)(c+1).wo(p)

      units(r)(c).r(p) := io.req(p)(r)(c)
    }

    for( p <- 0 until npri; r <- 0 until nrow ) rmsk(p)(r) := units(r)(ncol-1).eo(p)
    for( p <- 0 until npri; c <- 0 until ncol ) cmsk(p)(c) := units(nrow-1)(c).so(p)
    for( p <- 0 until npri; r <- 0 until nrow; c <- 0 until ncol ) grow(p)(r)(c) := units(r)(c).g(p)
  }

  io.gr := grow
  io.rmo := rmsk 
  io.cmo := cmsk

  def sp( req:Vec[Bool] ) = {
    VecInit((0 until req.size).map{ case i =>
      if( i==0 ) req(0)
      else{
        val preReq = req.slice(0,i).reduce(_|_)
        !preReq & req(i)
      } 
    })
  }

}


/** 仲裁请求、应答转化接口 
 *
 * 用户通过扩展该接口来实现不同的仲裁请求预处理和仲裁结果后处理
 */
trait ArbiterTransform{

  private var sub : Option[ArbiterTransform] = None

  /** 链接操作: 前一个ArbiterTransform包含后一个ArbiterTransform
   * @note `preproc`过程:操作符左侧的Transform先执行; `postproc`过程: 操作符右侧的Transform先执行
   */
  def ::(trans:ArbiterTransform) = {
    trans.sub = Some(this) 
    trans
  }

  /** 请求预处理
   * @param in 预处理输入信息序列。每个序列代表一个仲裁行为。序列中每个元素为由如下3个元素组成的tuple
   *   - req: Vec[Vec[Vec[Bool]]]类型。代表仲裁器请求。参考[[ArbiterCommon]]中关于req信号的定义
   *   - rmi: Vec[Vec[Bool]]类型。代表仲裁器行屏蔽信号。参考[[ArbiterCommon]]中关于rmi信号的定义
   *   - cmi: Vec[Vec[Bool]]类型。代表仲裁器列屏蔽信号。参考[[ArbiterCommon]]中关于cmi信号的定义
   *   - cptr: Vec[Vec[Bool]]类型。代表仲裁器列指针。参考[[ArbiterCommon]]中关于cptr信号的定义
   *   - rptr: Vec[Vec[Bool]]类型。代表仲裁器行指针。参考[[ArbiterCommon]]中关于rptr信号的定义
   * @return 返回新的信息序列。格式和`in`保持一致
   */
  def preproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) 
    : Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])]

  /** 应答后处理
   * @param in 后处理输入信息序列。每个序列代表一个仲裁行为。序列中每个元素为由如下3个元素组成的tuple
   *   - gnt: Vec[Vec[Vec[Bool]]]类型。代表仲裁器请求。参考[[ArbiterCommon]]中关于gnt信号的定义
   *   - rmo: Vec[Vec[Bool]]类型。代表仲裁器行屏蔽信号。参考[[ArbiterCommon]]中关于rmo信号的定义
   *   - cmo: Vec[Vec[Bool]]类型。代表仲裁器列屏蔽信号。参考[[ArbiterCommon]]中关于cmo信号的定义
   * @return 返回新的信息序列。格式和`in`保持一致
   */
  def postproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) 
    : Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])]

  /** 内部链式预处理函数 */
  def preprocess( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    val out = preproc(in)
    sub match {
      case None => out 
      case Some(s) => s.preproc( out )
    }
  }

  /** 内部链式后处理函数 */
  def postprocess( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    val out = sub match {
      case None => in 
      case Some(s) => s.postproc( in )
    }
    postproc( out )
  }

}

/** Strict-Priority Transform：无处理 */
class SPTrans extends ArbiterTransform{
  def preproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = in
  def postproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = in
}

/** 翻转Transform
 * @param colRev 是否列翻转 (搜索顺序从大到小)
 * @param rowRev 是否行翻转 (搜索顺序从大到小)
 * @param priRev 是否优先级翻转 (搜索顺序从大到小)
 */
class ReverseTrans(colRev:Boolean=false, rowRev:Boolean=false, priRev:Boolean=false) extends ArbiterTransform {

  def reverse2D( in:Vec[Vec[Bool]], revDim1:Boolean, revDim2:Boolean ) = {
    val res = Wire(chiselTypeOf(in))
    for( d1 <- 0 until in.size; d2 <- 0 until in(0).size ){
      val idxD1 = if( revDim1 ) in.size - 1 - d1 else d1
      val idxD2 = if( revDim2 ) in(0).size - 1 - d2 else d2 
      res(d1)(d2) := in(idxD1)(idxD2)
    }
    res
  }

  def reverse3D( in:Vec[Vec[Vec[Bool]]], revDim1:Boolean, revDim2:Boolean, revDim3:Boolean ) = {
    val res = Wire(chiselTypeOf(in))
    for( d1 <- 0 until in.size; d2 <- 0 until in(0).size; d3 <- 0 until in(0)(0).size ){
      val idxD1 = if( revDim1 ) in.size - 1 - d1 else d1
      val idxD2 = if( revDim2 ) in(0).size - 1 - d2 else d2 
      val idxD3 = if( revDim3 ) in(0)(0).size - 1 - d3 else d3 
      res(d1)(d2)(d3) := in(idxD1)(idxD2)(idxD3)
    }
    res
  }

  def reversePtr( ptr:Vec[Vec[Bool]], priRev:Boolean, ptrRev:Boolean ) = {
    if( ptr.size > 0 ){
      val res_1 = Wire(chiselTypeOf(ptr))
      for( p <- 0 until ptr.size ){
        val idxPtr = if( priRev ) ptr.size - 1 - p else p 
        res_1(p) := ptr(idxPtr)
      }
      
      if( ptrRev ){
        VecInit(res_1.map{ case p => 
          VecInit((true.B +: p.init).map(!_).reverse)
        })
      }else{
        res_1
      }
    }else{
      ptr
    }
  }
  
  def preproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    in.map { case (req, rmi, cmi, cptr, rptr) => 
      val res_req = reverse3D( req, priRev, rowRev, colRev )
      val res_rmi = reverse2D( rmi, priRev, rowRev )
      val res_cmi = reverse2D( cmi, priRev, colRev )
      val res_rptr = reversePtr( rptr, priRev, rowRev )
      val res_cptr = reversePtr( cptr, priRev, colRev )

      (res_req, res_rmi, res_cmi, res_cptr, res_rptr)
    }
  }

  def postproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    in.map { case (gr, rmo, cmo, cptr, rptr) => 
      val res_gr = reverse3D( gr, priRev, rowRev, colRev )
      val res_rmo = reverse2D( rmo, priRev, rowRev )
      val res_cmo = reverse2D( cmo, priRev, colRev )

      (res_gr, res_rmo, res_cmo, cptr, rptr)
    }
  }

}

class RR1DTrans extends ArbiterTransform {

  def preproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    in.flatMap { case (req, rmi, cmi, cptr, rptr) => 
      val npri = req.size 
      val nrow = rmi(0).size 
      val ncol = cmi(0).size

      val req0 = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))
      val req1 = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))
      for( p <- 0 until npri; r <- 0 until nrow; c <- 0 until ncol ){
        req0(p)(r)(c) := req(p)(r)(c) & (!cptr(p)(c))
        req1(p)(r)(c) := req(p)(r)(c)
      }

      val cmi0 = Wire(Vec(npri, Vec(ncol, Bool())))
      val cmi1 = Wire(Vec(npri, Vec(ncol, Bool())))
      for( p <- 0 until npri; c <- 0 until ncol ){
        cmi0(p)(c) := cmi(p)(c) & (!cptr(p)(c))
        cmi1(p)(c) := cmi(p)(c)
      }

      Seq( (req0, rmi, cmi0, cptr, rptr), (req1, rmi, cmi1, cptr, rptr) )
    }
  }
  def postproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    val Seq((g0,rmo0,cmo0, cptr0, rptr0), (g1,rmo1,cmo1, cptr1, rptr1)) = in
    val sel = rmo0(0)(0)
    val g_res = Mux( sel, g0, g1 )
    val rmo_res = Mux( sel, rmo0, rmo1 )
    val cmo_res = Mux( sel, cmo0, cmo1 )
    Seq((g_res, rmo_res, cmo_res, cptr0, rptr0))
  }
}

class Until01DTrans extends ArbiterTransform {
  def preproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    in.flatMap { case (req, rmi, cmi, cptr, rptr) => 
      val npri = req.size 
      val nrow = rmi(0).size 
      val ncol = cmi(0).size

      val req0 = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))
      val req1 = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))
      for( p <- 0 until npri; r <- 0 until nrow; c <- 0 until ncol ){
        req0(p)(r)(c) := req(p)(r)(c) & (!cptr(p)(c))
        req1(p)(r)(c) := req(p)(r)(c)
      }

      val cmi0 = Wire(Vec(npri, Vec(ncol, Bool())))
      val cmi1 = Wire(Vec(npri, Vec(ncol, Bool())))
      for( p <- 0 until npri; c <- 0 until ncol ){
        cmi0(p)(c) := cmi(p)(c) & (cptr(p)(c))
        cmi1(p)(c) := cmi(p)(c)
      }

      Seq( (req0, rmi, cmi0, cptr, rptr), (req1, rmi, cmi1, cptr, rptr) )
    }
  }
  def postproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    val Seq((g0,rmo0,cmo0, cptr0,rptr0), (g1,rmo1,cmo1,cptr1,rptr1)) = in

    val g0Full = g0(0)(0).last
    val gnt = VecInit(g0(0)(0).zip(g1(0)(0)).map{ case (gg0, gg1) => gg0 | gg1 })
    val g_res = Mux( 
      g0Full, 
      VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(gnt)))),
      g0
    )
    val rmo_res = rmo0
    val cmo_res = Mux( 
      g0Full,
      VecInit(cmo0.zip(cmo1).map{case (cp0,cp1) =>
        VecInit(cp0.zip(cp1).map {case (cc0,cc1) => cc0|cc1})
      }),
      cmo0
    )
    Seq((g_res, rmo_res, cmo_res, cptr0, rptr0))
  }
}

class RRDPATrans extends ArbiterTransform{
  def preproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {

    in.map { case (req, rmi, cmi, cptr, rptr) => 
      val npri = req.size 
      val nrow = req(0).size 
      val ncol = req(0)(0).size
      
      val req_i = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))
      val rmi_i = Wire(Vec(npri, Vec(nrow, Bool())))

      for(p <- 0 until npri){
        val sel = (0 until nrow).map{ case r => comlib.util.rrShiftL(rptr(p).asUInt, r) }
        req_i(p) := VecInit(sel.map{ case s => Mux1H(s, req(p)) })
        rmi_i(p) := VecInit(sel.map{ case s => Mux1H(s, rmi(p)) })
      }

      (req_i, rmi_i, cmi, cptr, rptr)
    }
  }
  def postproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = {
    
    in.map { case (gr, rmo, cmo, cptr, rptr) => 
      val npri = gr.size 
      val nrow = gr(0).size 
      val ncol = gr(0)(0).size
      
      val gr_i = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))
      val rmo_i = Wire(Vec(npri, Vec(nrow, Bool())))

      for(p <- 0 until npri){
        val sel_s = (0 until nrow).map{ case r => comlib.util.rrShiftL(rptr(p).asUInt, r) }
        val sel = (0 until nrow).map{ case c =>
          VecInit((0 until nrow).map{ case r => sel_s(r)(c)}).asUInt
        }
        gr_i(p) := VecInit(sel.map{ case s => Mux1H(s, gr(p)) })
        rmo_i(p) := VecInit(sel.map{ case s => Mux1H(s, rmo(p)) })
      }

      (gr_i, rmo_i, cmo, cptr, rptr)
    }
  }
}

class RR2DTrans(cptr:Vec[UInt], rptr:Vec[UInt]) extends ArbiterTransform {
  def preproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = in
  def postproc( in:Seq[(Vec[Vec[Vec[Bool]]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]], Vec[Vec[Bool]])] ) = in
}

class EArbiter( ncol:Int, nrow:Int=1, npri:Int=1, ncp:Int=0, nrp:Int=0 )
  (implicit val trans:ArbiterTransform=new SPTrans, val action:SelectAction=new SelectFind1) 
  extends ArbiterCommon(ncol, nrow, npri, ncp, nrp) {

  val pre_in = Seq((io.req, io.rmi, io.cmi, io.cptr, io.rptr))
  val pre_out = trans.preprocess( pre_in )

  val arbs = pre_out.map { case (r, ri, ci, _, _) => Module(new SelectMesh(r(0)(0).size, r(0).size, r.size)(action)) }
  arbs.zip(pre_out).foreach { case (arb, (r, ri, ci, _, _)) =>  
    arb.io.req := r 
    arb.io.rmi := ri 
    arb.io.cmi := ci
  }

  val post_in = arbs.map{ case arb => (arb.io.gr, arb.io.rmo, arb.io.cmo, io.cptr, io.rptr) }
  val post_out = trans.postprocess( post_in )(0)

  io.gr := post_out._1 
  io.rmo := post_out._2 
  io.cmo := post_out._3

  def setAction(act:SelectAction) = {
    arbs.foreach{ case arb => arb.action_i = act}
    this
  }

}

object EArbiter{

  /** 标准的SP调度
   * @param req 输入请求
   * @param action 选择行为。默认为SelectFind1
   * @return EArbiter实例
   * 
   * 调度器内部使用false.B来连接cmi/rmi信号
   *
   * 该函数有多种输入参数选择
   * @group Module
   * @example 
   {{
     EArbiter.SP("b1110").gnt.asUInt   // "b0010"
     EArbiter.SP("b0000").gnt.asUInt   // "b0000"
     EArbiter.SP(Seq(false.B, true.B, true.B)).gnt.asUInt   // "b010"
   }}
   */
  def SP(req:Vec[Bool], action:SelectAction):EArbiter = {
    val arb = Module( new EArbiter(req.size)(action=action) )
    arb.io.rmi := arb.rmi(false.B)
    arb.io.cmi := arb.cmi(false.B)
    arb.io.req := arb.req(req)
    arb
  }
  /** @group Module */
  def SP(req:Vec[Bool]):EArbiter = SP(req, new SelectFind1)
  /** @group Module */
  def SP(req:UInt, action:SelectAction):EArbiter = SP(VecInit(req.asBools),action)
  /** @group Module */
  def SP(req:UInt):EArbiter = SP(VecInit(req.asBools), new SelectFind1)
  /** @group Module */
  def SP(req:Seq[Bool], action:SelectAction):EArbiter = SP(VecInit(req), action)
  /** @group Module */
  def SP(req:Seq[Bool]):EArbiter = SP(VecInit(req), new SelectFind1)


  /** 反向SP调度
   * @group Module
   */
  def SPReverse(req:Vec[Bool], action:SelectAction):EArbiter = {
    val revTrans = new ReverseTrans(true)
    val arb = Module( new EArbiter(req.size)(trans=revTrans, action=action) )
    arb.io.rmi := arb.rmi(false.B)
    arb.io.cmi := arb.cmi(false.B)
    arb.io.req := arb.req(req)
    arb
  }
  /** @group Module */
  def SPReverse(req:Vec[Bool]):EArbiter = SP(req, new SelectFind1)
  /** @group Module */
  def SPReverse(req:Seq[Bool], action:SelectAction):EArbiter = SP(VecInit(req), action)
  /** @group Module */
  def SPReverse(req:Seq[Bool]):EArbiter = SP(VecInit(req), new SelectFind1)
  /** @group Module */
  def SPReverse(req:UInt, action:SelectAction):EArbiter = SP(VecInit(req.asBools), action)
  /** @group Module */
  def SPReverse(req:UInt):EArbiter = SP(VecInit(req.asBools), new SelectFind1)

  def SPDPA(req:Vec[Vec[Bool]], rmi:Vec[Bool], cmi:Vec[Bool]):EArbiter = {
    val arb = Module( new EArbiter(req(0).size, req.size) )
    arb.io.rmi := VecInit(Seq.fill(1)(rmi))
    arb.io.cmi := VecInit(Seq.fill(1)(cmi))
    arb.io.req := VecInit(Seq.fill(1)(req))
    arb
  }
  def SPDPA(req:Vec[Vec[Bool]], cmi:Vec[Bool]):EArbiter = {
    val arb = Module( new EArbiter(req(0).size, req.size) )
    arb.io.rmi := VecInit(Seq.fill(1)(VecInit(Seq.fill(req.size)(false.B))))
    arb.io.cmi := VecInit(Seq.fill(1)(cmi))
    arb.io.req := VecInit(Seq.fill(1)(req))
    arb
  }
  def SPDPA(req:Vec[Vec[Bool]], rmi:Vec[Bool], isRmi:Boolean):EArbiter = {
    val arb = Module( new EArbiter(req(0).size, req.size) )
    arb.io.rmi := VecInit(Seq.fill(1)(rmi))
    arb.io.cmi := VecInit(Seq.fill(1)(VecInit(Seq.fill(req(0).size)(false.B))))
    arb.io.req := VecInit(Seq.fill(1)(req))
    arb
  }
  def SPDPA(req:Vec[Vec[Bool]]):EArbiter = {
    val arb = Module( new EArbiter(req(0).size, req.size) )
    arb.io.rmi := VecInit(Seq.fill(1)(VecInit(Seq.fill(req.size)(false.B))))
    arb.io.cmi := VecInit(Seq.fill(1)(VecInit(Seq.fill(req(0).size)(false.B))))
    arb.io.req := VecInit(Seq.fill(1)(req))
    arb
  }

  def RRDPA(req:Vec[Vec[Bool]], ptr:Vec[Bool], rmi:Vec[Bool], cmi:Vec[Bool]):EArbiter = {
    val nrow = req.size 
    val ncol = req(0).size
    val arb = Module(new EArbiter(ncol, nrow, nrp=nrow)(trans=new RRDPATrans))
    arb.io.req(0) := req
    arb.io.rmi(0) := rmi 
    arb.io.cmi(0) := cmi
    arb.io.rptr(0) := ptr
    arb
  }

  def RRDPA(req:Vec[UInt], ptr:UInt, rmi:UInt, cmi:UInt):EArbiter = {
    val nrow = req.size 
    val ncol = req(0).getWidth

    val ptr_i = Wire(UInt(nrow.W))
    val rmi_i = Wire(UInt(nrow.W))
    val cmi_i = Wire(UInt(ncol.W))

    val req_i = VecInit(req.map{ case r => VecInit(r.asBools) })
    ptr_i := ptr
    rmi_i := rmi
    cmi_i := cmi

    val arb = Module(new EArbiter(ncol, nrow, nrp=nrow)(trans=new RRDPATrans))
    arb.io.req(0) := req_i
    arb.io.rmi(0) := VecInit(rmi_i.asBools)
    arb.io.cmi(0) := VecInit(cmi_i.asBools)
    arb.io.rptr(0) := VecInit(ptr_i.asBools)
    arb
  }

  /** 标准RoundRobin算法
   * @group Module
   * @param req 请求。第i个元素代表第i个请求；true.B表示有请求
   * @param ptr 温度计编码的指针。指向最优先被选择的请求位置
   * @return 返回[[EArbiter]]模块实例
   */
  def RoundRobin(req:Vec[Bool], ptr:Vec[Bool]):EArbiter = {
    val arb = Module( new EArbiter(req.size, ncp=ptr.size)(trans=new RR1DTrans) )
    arb.io.rmi := VecInit(Seq.fill(1)(VecInit(false.B)))
    arb.io.cmi := VecInit(Seq.fill(1)(VecInit(Seq.fill(req.size)(false.B))))
    arb.io.req := VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(req))))
    arb.io.cptr := VecInit(Seq.fill(1)(ptr))
    arb
  }
  /** @group Module */
  def RoundRobin(req:UInt, ptr:UInt):EArbiter = RoundRobin(VecInit(req.asBools), VecInit(ptr.asBools)) 
  /** @group Module */
  def RoundRobin(req:Vec[Bool], ptr:UInt):EArbiter = RoundRobin(req, VecInit(ptr.asBools))

  def RoundRobinReverse(req:Vec[Bool], ptr:Vec[Bool]):EArbiter = {
    val rrTrans = new RR1DTrans
    val revTrans = new ReverseTrans(true)
    val arb = Module( new EArbiter(req.size, ncp=ptr.size)(trans=revTrans::rrTrans) )
    arb.io.rmi := VecInit(Seq.fill(1)(VecInit(false.B)))
    arb.io.cmi := VecInit(Seq.fill(1)(VecInit(Seq.fill(req.size)(false.B))))
    arb.io.req := VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(req))))
    arb.io.cptr := VecInit(Seq.fill(1)(ptr))
    arb
  }

  def RoundRobinReverse(req:UInt, ptr:UInt):EArbiter = RoundRobinReverse( VecInit(req.asBools), VecInit(ptr.asBools) )

  /** 标准Unitl0算法
   * @group Module
   * @param req 请求。第i个元素代表第i个请求；true.B表示有请求
   * @param ptr 温度计编码的指针。指向最优先被选择的请求位置
   * @return 返回[[EArbiter]]模块实例
   */
  def Until0(req:Vec[Bool], ptr:Vec[Bool]) : EArbiter = {
    val arb = Module( new EArbiter(req.size, ncp=ptr.size)(trans=new Until01DTrans, action=new SelectUntil0) )
    arb.io.rmi := VecInit(Seq.fill(1)(VecInit(false.B)))
    arb.io.cmi := VecInit(Seq.fill(1)(VecInit(Seq.fill(req.size)(false.B))))
    arb.io.req := VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(req))))
    arb.io.cptr := VecInit(Seq.fill(1)(ptr))
    arb
  }
  /** @group Module */
  def Until0(req:Vec[Bool]):EArbiter = Until0(req, VecInit(Seq.fill(req.size)(false.B)))
  /** @group Module */
  def Until0(req:UInt, ptr:UInt):EArbiter = Until0(VecInit(req.asBools), VecInit(ptr.asBools)) 
  /** @group Module */
  def Until0(req:Vec[Bool], ptr:UInt):EArbiter = Until0(req, VecInit(ptr.asBools))
}



/** 2D选择单元 
 *
 * 用于组织构建仲裁算法的基本单元
 * @param priNum 优先级个数
 * @param mode 选择策略
 *   - 0: 选择第一个"1"
 *   - 1: 选择第一个"0"
 *   - 2: 选择until第一个"1"(不包含)
 *   - 3: 选择until第一个"0"(不包含)
 *   - 4: 函数自定义
 *
 * @group Implement
 */
class SelectUnit(priNum:Int, mode:Int=0) extends Module {

  class SelectUnitIO(val priNum:Int) extends Bundle {
    /** 输入请求
     * true.B表示有请求。第i个元素代表优先级i的请求。 默认序号越小对应的优先级越高
     * @group IO
     */
    val r = Input(Vec(priNum, Bool()))

    /** 东向输入屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val ei = Input(Vec(priNum, Bool()))

    /** 东向输出屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val eo = Output(Vec(priNum, Bool()))

    /** 南向输入屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val si = Input(Vec(priNum, Bool()))

    /** 南向输出屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val so = Output(Vec(priNum, Bool()))

    /** 西向输入屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val wi = Input(Vec(priNum, Bool()))

    /** 西向输出屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val wo = Output(Vec(priNum, Bool()))

    /** 北向输入屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val ni = Input(Vec(priNum, Bool()))

    /** 北向输出屏蔽
     * true.B表示屏蔽。第i个元素代表优先级i-1的请求。默认序号越小对应的优先级越高
     * @group IO
     */
    val no = Output(Vec(priNum, Bool()))

    /** 应答
     * true.B表示有应答。第i个元素代表优先级i的应答。默认序号越小对应的优先级越高
     * @group IO
     */
    val g = Output(Vec(priNum, Bool()))
  }

  val io = IO( new SelectUnitIO(priNum))

  // 主体代码
  for( p <- 0 until priNum ){

    // 请求授权条件
    //  - 当前优先级必须有请求
    //  - 西侧屏蔽条件：高于等于当前优先级的屏蔽都会使当前节点不可选择
    //  - 北侧屏蔽条件：高于等于当前优先级的屏蔽都会使当前节点不可选择
    //  - 东侧屏蔽条件：高于当前优先级的屏蔽，会使当前节点不可选择
    //  - 南侧屏蔽条件：高于当前优先级的屏蔽，会使当前节点不可选择
    val gi = mode match {
      case 0 => io.r(p) & !(io.wi.slice(p, priNum).reduce(_|_)) & !(io.ni.slice(p,priNum).reduce(_|_))
      case 1 => !io.r(p) & !(io.wi.slice(p, priNum).reduce(_|_)) & !(io.ni.slice(p,priNum).reduce(_|_))
      case 3 =>   // 选中一个0，则后续都不能被选中，故需要使用wi来屏蔽; ni仅用于屏蔽当前节点是否可以参与选择
        io.r(p) & !(io.wi.slice(p, priNum).reduce(_|_)) & !(io.ni.slice(p,priNum).reduce(_|_))
    }

    if( p == priNum - 1 ) 
      io.g(p) := gi
    else 
      io.g(p) := gi & !(io.ei.slice(p+1,priNum).reduce(_|_)) & !(io.si.slice(p+1,priNum).reduce(_|_))
    
    mode match {

      case 0 | 1 =>
        io.eo(p) := io.g(p) | io.wi(p)  // 东侧屏蔽条件：当前无仲裁，或者西侧对应优先级有屏蔽
        io.so(p) := io.g(p) | io.ni(p)  // 南侧屏蔽条件：当前无仲裁，或者西侧对应优先级有屏蔽
        io.wo(p) := io.g(p) | io.ei(p)  // 西侧屏蔽条件：当前无仲裁，或者东侧对应优先级有屏蔽
        io.no(p) := io.g(p) | io.si(p)  // 北侧屏蔽条件：当前无仲裁，或者东侧对应优先级有屏蔽

      case 3 =>
        io.eo(p) := !io.r(p)&(!io.ni(p)) | io.wi(p)   // 北侧即使屏蔽了，当前
        io.so(p) := io.g(p) | io.ni(p)
        io.wo(p) := io.g(p) | io.ei(p)
        io.no(p) := io.g(p) | io.si(p)

    }

    
  }

}



/** RoundRobin实现类
 *
 * 模块实例化推荐使用[[Arbiter]]
 * 
 * Arguments
 * @param ncol 列数(>=1)
 * @param nrow 行数(>=1)
 * @param npri 优先级个数
 * @param colMode 列选择策略
 *   - 0: SP选择
 *   - 1: RR选择，无指针
 *   - 2: RR选择，提供指针
 * @param isColRev 列选择是否从大到小
 * @param rowMode 行选择策略
 *   - 0: SP选择
 *   - 1: RR选择，无指针
 *   - 2: RR选择，提供指针
 * @param isRowRev 行选择是否从大到小
 * @param isPriRev 优先级选择是否从大到小
 * @param selMode 选择策略
 *   - 0: 选择第一个"1"
 *   - 1: 选择第一个"0"
 *   - 2: 选择until第一个"1"(不包含)
 *   - 3: 选择until第一个"0"(不包含)
 *   - 4: 选择to第一个"1"(包含)
 *   - 5: 选择to第一个"0"(包含)
 * @group Constructor
 *
 * @groupdesc Signal-Extends 提供一些列函数，把用户提供的各种格式的信号格式转化为[[Arbiter]]可以接收的输入信号格式。这些函数用于在各种应用下简化用户的输入。
 *
 */
class Arbiter(ncol:Int=4, nrow:Int=1, npri:Int=1, 
  colMode : Int = 0, 
  isColRev : Boolean = false,
  rowMode : Int = 0,
  isRowRev : Boolean = false,
  isPriRev : Boolean = false,
  selMode : Int = 0,
) extends Module{

  protected val incol = if( colMode==1||colMode==2 ) 2*ncol-1 else ncol   // The column number of innser select unit matrix
  protected val inrow = if( rowMode==1||rowMode==2 ) 2*nrow-1 else nrow   // The row number of innser select unit matrix
  
  // ======================  Variable define
  protected val grant = Wire(Vec(npri, Vec(nrow, Vec(ncol, Bool()))))   // Inner arbite grant
  
  // =============    扩展功能：内部指针    ============
  protected val cpointers = if( colMode==2 ) RegInit(VecInit(Seq.fill(npri)(VecInit(Seq.fill(ncol-1)(false.B))))) else null
  protected val rpointers = if( rowMode==2 ) RegInit(VecInit(Seq.fill(npri)(VecInit(Seq.fill(nrow-1)(false.B))))) else null

  /**
   * @groupdesc Output 模块输出信号。[[Arbiter]]定义了很多的输出信号，在实际使用中可以根据实际情况选择使用，并不需要链接所有的输出信号
   */
  class RoundRobinIO extends Bundle{
    /** 行屏蔽信号。bit[i][j]代表对第i个优先级的第j行是否屏蔽；1.U表示需要屏蔽。
     * @note 建议使用RoundRobin.rmsk函数进行赋值
     * @group Input
     */
    val rmsk = Input( Vec(npri, Vec(inrow, Bool())) )

    /** 列屏蔽信号。bit[i]代表对第i列是否屏蔽；1.U表示需要屏蔽 
     * @note 建议使用RoundRobin.cmsk函数进行赋值
     * @group Input
     */
    val cmsk = Input( Vec(npri, Vec(incol, Bool())) )

    /** 请求信号。bit[i][j][k]表示对优先级i、行j、列k的请求;  1.U表示有请求 
     * @note 建议使用RoundRobin.req函数进行赋值
     * @group Input
     */
    val r = Input( Vec(npri, Vec(inrow, Vec(incol, Bool()))) )

    /** 仲裁器内部Grant信号(会进行进一步的展开)
     *   - bit[i][j][k]表示对优先级i、行j、列k的应答
     *   - true.B: 表示有应答; false.B: 表示无应答 
     * @note 该信号为仲裁器内部生成的Grant信息，需要一定的转化才能生成为用户看到的Grant信号。该信号一般用于内部调试和特殊的仲裁构造来使用；一般用户建议直接使用[[gnt]]信号
     * @group Output
     */
    val g = Output( Vec(npri, Vec(inrow, Vec(incol, Bool()))) )

    /** 仲裁结果信号
     * == 编码格式 ==
     * 该仲裁结果随不同的参数场景而略有不同，符合一般场景的使用规则:
     *   - npri=1 & nrow=1: 返回一个Vec(ncol, Bool())类型的Grant
     *     - 该Grant为onehot编码格式的仲裁结果信息
     *   - npri=1 & nrow!=1: 返回一个Vec(ncol, Vec(nrow, Bool()))类型的Grant
     *     - 第一层Vec代表总计ncol列的Grant信息(即每个输出端口的Grant信息)
     *     - 第二层Vec代表对应的onehot编码的Grant内容
     *   - npri!=1 & nrow=1: 返回一个Vec(npri, Vec(ncol, Bool()))类型的Grant
     *     - 第一层Vec代表总计npri个优先级的Grant信息
     *     - 第二层Vec代表对应的onehot编码的Grant内容
     *   - npri!=1 & nrow!=1: 返回一个Vec(npri, Vec(ncol, Vec(nrow, Bool())))类型的Grant
     *     - 第一层Vec代表总计npri个优先级的Grant信息
     *     - 第二层Vec代表总计ncol列的Grant信息(即每个输出端口的Grant信息)
     *     - 第三层Vec代表对应的onehot编码的Grant内容
     *   - 每个Grant都是一个Vec[Bool]类型的数据结构，采用onehot编码。位置i为"true.B"，则代表位置i有Grant
     * 
     * == 常见场景下gnt格式 ==
     * [[Arbiter]]中预定义的场景调度场景下的gnt编码如下:
     *   - sp: npri=1 & nrow=1编码
     *   - rr: npri=1 & nrow=1编码
     *   - squzzueBubble: npri=1编码
     *
     * @note 每个Grant可以使用.asUInt()来转化为UInt的onehot编码；也可以通过Arbiter.onehot2binary函数转化为UInt类型的二进制编码
     * @group Output
     */
    val gnt = 
      if( npri == 1 ){
        if( nrow == 1 ) Output( Vec(ncol, Bool()) )
        else Output( Vec(ncol, Vec(nrow, Bool())) )
      }else{
        if( nrow == 1 ) Output( Vec(npri, Vec(ncol, Bool())) )
        else Output( Vec(npri, Vec(ncol, Vec(nrow, Bool()))) )
      }

    /** 经过选择后的行屏蔽信号。bit[i]代表对第i行是否屏蔽；1.U表示需要屏蔽 
     * @group Output
     */
    val rmsko = Output( Vec(npri, Vec(nrow, Bool())) )

    /** 仲裁器原始rmsko输出(会进行展开) 
     * bit[i][j]代表第i个优先级第j行的rmsko输出; "true.B"代表有屏蔽 
     * @group Output
     */
    val rmo = Output( Vec(npri, Vec(inrow, Bool())) )

    /** 经过选择后的列屏蔽信号。bit[i]代表对第i行是否屏蔽；1.U表示需要屏蔽 
     * @group Output
     */
    val cmsko = Output( Vec(npri, Vec(ncol, Bool())) )

    /** 仲裁器原始cmsko输出(会进行展开)
     * bit[i][j]代表第i个优先级第j列的cmsko输出; "true.B"代表有屏蔽 
     * @group Output
     */
    val cmo = Output( Vec(npri, Vec(incol, Bool())) )

    /** 以{pri, row}为维度的onehot格式的仲裁结果
     * @group Output
     */
    val gr = Output( Vec(npri, Vec(nrow, Vec(ncol, Bool()))) )

    /** 以{pri, col}为维度的onehot格式的仲裁结果
     * @group Output 
     */
    val gc = Output( Vec(npri, Vec(ncol, Vec(nrow, Bool()))) )

    /** 以{col}为维度的onehot格式的仲裁结果
     * @group Output 
     */
    val gc0 = Output( Vec(ncol, Vec(nrow, Bool())) )

    /* 行指针: 优选哪一行的指示
      * 
      * 该轮询指针采用“温度计”编码：
      *  - "b0000_0000" => 从0号位置开始，由小到大选择
      *  - "b0000_0001" => 从1号位置开始，选择顺序为[1,2,3,4,5,6,7,0]
      *  - "b0000_0011" => 从2号位置开始，选择顺序为[2,3,4,5,6,7,0,1]
      *  - "b0000_0111" => 从3号位置开始，选择顺序为[3,4,5,6,7,0,1,2]
      *  - "b0000_1111" => 从4号位置开始，选择顺序为[4,5,6,7,0,1,2,3]
      *  - "b0001_1111" => 从5号位置开始，选择顺序为[5,6,7,0,1,2,3,4]
      *  - "b0011_1111" => 从6号位置开始，选择顺序为[6,7,0,1,2,3,4,5]
      *  - "b0111_1111" => 从7号位置开始，选择顺序为[7,0,1,2,3,4,5,6]
      * 温度计编码 <=> 二进制编码的转化参考[[Arbiter.temp2Bin]]和[[Arbiter]]
      *
     */
  }
  
  /** IO */
  val io = IO(new RoundRobinIO)

  protected val units =   // Inner select unit matrix
    for( x <- 0 until inrow ) 
      yield for( y <- 0 until incol ) 
        yield Module(new SelectUnit(npri, selMode))

  // input to units connect
  for( r <- 0 until inrow; c <- 0 until incol; p <- 0 until npri ){

    // Reverse process
    val cc = if( isColRev ) ncol-1-c else c
    val rr = if( isRowRev ) nrow-1-r else r
    val pp = if( isPriRev ) npri-1-p else p

    if( r == 0 ){ // First row special process
      if( colMode == 2 && cc < ncol-1 ){
        units(r)(c).io.ni(p) := io.cmsk(pp)(cc) | cpointers(pp)(cc%ncol)
      }else{
        units(r)(c).io.ni(p) := io.cmsk(pp)(cc)
      }
    }else{  // Matrix Inner connect
      units(r)(c).io.ni(p) := units(r-1)(c).io.so(p)  // Last south -> Current north
    }

    if( c == 0 ){ // First column
      if( rowMode == 2 && rr < nrow-1 ){
        units(r)(c).io.wi(p) := io.rmsk(pp)(rr) | rpointers(pp)(rr%nrow)
      }else{
        units(r)(c).io.wi(p) := io.rmsk(pp)(rr)
      }
    }else{  // Matrix Inner connect
      units(r)(c).io.wi(p) := units(r)(c-1).io.eo(p)  // Last east -> current west
    }

    // Matrix Inner connect
    if( r == inrow-1 ){  // Last row
      units(r)(c).io.si(p) := false.B
    }else{  
      units(r)(c).io.si(p) := units(r+1)(c).io.no(p)
    }

    // Matrix Inner connect
    if( c == incol-1 ){  // Last column
      units(r)(c).io.ei(p) := false.B
    }else{
      units(r)(c).io.ei(p) := units(r)(c+1).io.wo(p)
    }

    // Request connect
    units(r)(c).io.r(p) := io.r(pp)(rr)(cc)

  }

  for( p <- 0 until npri; r <- 0 until inrow ){
    val pp = if( isPriRev ) npri-1-p else p
    io.rmo(pp)(r) := units(r)(incol-1).io.eo(pp)
  }

  for( p <- 0 until npri; c <- 0 until incol ){
    val pp = if( isPriRev ) npri-1-p else p
    io.cmo(pp)(c) := units(inrow-1)(c).io.so(pp)
  }


  // g connection
  for( p <- 0 until npri; r <- 0 until inrow; c <- 0 until incol ){
    io.g(p)(r)(c) := units(r)(c).io.g(p)
  }


  // unit matrix to Output connect
  for( p <- 0 until npri ){
    val pp = if( isPriRev ) npri-1-p else p

    // Grant process
    for( r <- 0 until nrow; c <- 0 until ncol ){
      val rr = if( isRowRev ) nrow-1-r else r
      val cc = if( isColRev ) ncol-1-c else c

      if(rr+nrow > inrow-1){ // if( rr == nrow-1 ){ // Note: 这里需要考虑行SP策略下，是无rr+nrow的
        if( cc+ncol > incol-1 )  //if( cc == ncol-1 ) Note: 这里需要考虑在SP策略下，是无cc+ncol的。所以不能简单cc==ncol-1
          io.gr(p)(r)(c) := units(rr)(cc).io.g(pp) 
        else
          io.gr(p)(r)(c) := units(rr)(cc).io.g(pp) | units(rr)(cc+ncol).io.g(pp)
      }else{
        if( cc+ncol > incol-1 )
          io.gr(p)(r)(c) := units(rr)(cc).io.g(pp) | units(rr+nrow)(cc).io.g(pp)
        else
          io.gr(p)(r)(c) := units(rr)(cc).io.g(pp) | units(rr)(cc+ncol).io.g(pp) | units(rr+nrow)(cc).io.g(pp) | units(rr+nrow)(cc+ncol).io.g(pp)
      }

    }

    // rmsko process
    for( r <- 0 until nrow ){
      val rr = if( isRowRev ) nrow-1-r else r
      if( rr+nrow > inrow-1 )
        io.rmsko(p)(r) := units(rr)(incol-1).io.eo(pp) 
      else
        io.rmsko(p)(r) := units(rr)(incol-1).io.eo(pp) | units(rr+nrow)(incol-1).io.eo(pp)
    }

    // cmsko process
    for( c <- 0 until ncol ){
      val cc = if( isColRev ) ncol-1-c else c
      if( cc+ncol > incol-1 )
        io.cmsko(p)(c) := units(inrow-1)(cc).io.so(pp)
      else
        io.cmsko(p)(c) := units(inrow-1)(cc).io.so(pp) | units(inrow-1)(cc+ncol).io.so(pp)
    }
    
  }


  //========================================================    Other output signals    =============================================
  grant := io.gr

  for( p <- 0 until npri ){
    // Grant as column dimension
    for( r <- 0 until nrow; c <- 0 until ncol ) io.gc(p)(c)(r) := io.gr(p)(r)(c)
    // Grant as row dimension with binary format
  }

  if( npri == 1 ){
    if( nrow == 1 ) io.gnt := io.gr(0)(0)
    else io.gnt := io.gc(0)
  }else{
    if( nrow == 1 ) io.gnt := VecInit( io.gr.map { case gp => gp(0) } )
    else io.gnt := io.gc
  }

  io.gc0 := io.gc(0)

  //=============================================================    Update Pointer    ==============================================
  if( colMode == 2 )
    (0 until npri).foreach {case p =>
      val pp = if( isPriRev ) npri-1-p else p
      cpointers(p) := Mux( io.cmo(pp)(ncol-1), VecInit(io.cmo(pp).slice(ncol, incol)), VecInit(io.cmo(pp).slice(0, ncol-1) ))
    }

  if( rowMode == 2 )
    (0 until npri).foreach {case p =>
      val pp = if( isPriRev ) npri-1-p else p
      rpointers(p) := Mux( io.rmo(pp)(nrow-1), VecInit(io.rmo(pp).slice(nrow, inrow)), VecInit(io.rmo(pp).slice(0, nrow-1) ))
    }


  //==================================================================================================================================
  //====                                            Function                                                                     =====
  //==================================================================================================================================
  def gr(ff:Boolean=false) = if(ff) RegNext(grant) else grant 

  def gc(ff:Boolean=false) = {
    val res = Wire( Vec(npri, Vec(ncol, Vec(nrow, Bool()))) )
    for( p <- 0 until npri; r <- 0 until nrow; c <- 0 until ncol ) res(p)(c)(r) := grant(p)(r)(c)
    if( ff ) RegNext(res) else res
  }

  //======================================================================================
  //=====                                  矩阵扩充操作                               =====
  //======================================================================================

  protected def msk( ptr:Vec[Vec[Bool]], cmsk:Vec[Vec[Bool]], isRow:Boolean=false ):Vec[Vec[Bool]] = {

    val scope = if( isRow ) inrow else incol

    VecInit( (0 until npri).map{ case p =>
      VecInit( ( 0 until scope ).map { case c =>

        // ptr位置计算
        val p1 = if( p < ptr.size ) p else p%ptr.size 
        val pp = if( c < ptr(p1).size ) ptr(p1)(c) else false.B

        // cmsk位置计算
        val c1 = if( p < cmsk.size ) p else p%cmsk.size 
        val c2 = if( c < cmsk(c1).size ) c else c%cmsk(c1).size

        // 最终返回结果
        pp & !cmsk(c1)(c2)
      }) 
    } )
  } 

  /** @group Signal-Extends */
  def cmsk( m:Vec[Vec[Bool]] ):Vec[Vec[Bool]] = msk( VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B)))), m, false )
  /** 列屏蔽信息
   * @param m 屏蔽信息。支持3种格式
   *   - Bool格式：是否同时屏蔽所有优先级的所有列
   *   - Vec[Bool]格式: 同时屏蔽对应列的所有优先级。第i个位置代表对第i列的屏蔽
   *   - Vec[Vec[Bool]]格式：bit[i][j]代表对优先级i的第j列的屏蔽
   *   - true.B表示屏蔽；false.B表示不屏蔽
   * @param ptr 列指针
   *   - 第一层Vec代表多个优先级
   *   - 第二层Vec代表一个温度计编码的指针值
   *   - 默认不考虑指针
   * @return 扩展后的仲裁器内部所需的cmsk信号格式。可以直接赋值给cmsk信号，例如：arb.io.cmsk := arb.cmsk(...)
   * @group Signal-Extends 
   */
  def cmsk( m: Vec[Vec[Bool]], ptr:Vec[Vec[Bool]] ):Vec[Vec[Bool]] = msk( ptr, m, false )
  /** @group Signal-Extends */
  def cmsk( m: => Vec[Bool] ): Vec[Vec[Bool]] = msk( VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B)))), VecInit(Seq.fill(1)(m)), false )
  /** @group Signal-Extends */
  def cmsk( m: => Vec[Bool], ptr:Vec[Vec[Bool]] ): Vec[Vec[Bool]] = msk( ptr, VecInit(Seq.fill(1)(m)), false )
  /** @group Signal-Extends */
  def cmsk( m:Bool ): Vec[Vec[Bool]] = VecInit(Seq.fill(npri)(VecInit(Seq.fill(incol)(m))))
  /** @group Signal-Extends */
  def cmsk( m:Bool, ptr:Vec[Vec[Bool]] ): Vec[Vec[Bool]] = msk( ptr, VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(m)))), false )
  /** @group Signal-Extends */
  def rmsk( m:Vec[Vec[Bool]] ):Vec[Vec[Bool]] = msk( VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B)))), m, true )
  /** 行屏蔽信息
   * @param m 屏蔽信息。支持3种格式
   *   - Bool格式：是否同时屏蔽所有优先级的所有行
   *   - Vec[Bool]格式: 同时屏蔽对应行的所有优先级。第i个位置代表对第i行的屏蔽
   *   - Vec[Vec[Bool]]格式：bit[i][j]代表对优先级i的第j行的屏蔽
   *   - true.B表示屏蔽；false.B表示不屏蔽
   * @param ptr 列指针
   *   - 第一层Vec代表多个优先级
   *   - 第二层Vec代表一个温度计编码的指针值
   *   - 默认不考虑指针
   * @return 扩展后的仲裁器内部所需的rmsk信号格式。可以直接赋值给rmsk信号，例如：arb.io.rmsk := arb.rmsk(...)
   * @group Signal-Extends 
   */
  def rmsk( m:Vec[Vec[Bool]], ptr:Vec[Vec[Bool]] ):Vec[Vec[Bool]] = msk( ptr, m, true )
  /** @group Signal-Extends */
  def rmsk( m: => Vec[Bool] ): Vec[Vec[Bool]] = VecInit(Seq.fill(npri)(m))
  /** @group Signal-Extends */
  def rmsk( m: => Vec[Bool], ptr:Vec[Vec[Bool]] ): Vec[Vec[Bool]] = msk( ptr, VecInit(Seq.fill(1)(m)), true )
  /** 把m扩展到rmsk所需的所有bit中
   * @group Signal-Extends */
  def rmsk( m:Bool ): Vec[Vec[Bool]] = VecInit(Seq.fill(npri)(VecInit(Seq.fill(inrow)(m))))
  /** @group Signal-Extends */
  def rmsk( m:Bool, ptr:Vec[Vec[Bool]] ): Vec[Vec[Bool]] = msk( ptr, VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(m)))), true )
  /** @group Signal-Extends */
  def cptr(ptr:Vec[Vec[Bool]]):Vec[Vec[Bool]] = msk(ptr, VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B)))), false)
  /** @group Signal-Extends */
  def cptr(ptr:Vec[Vec[Bool]], cmsk:Vec[Vec[Bool]]):Vec[Vec[Bool]] = msk( ptr, cmsk, false )
  /** @group Signal-Extends */
  def cptr(ptr: => Vec[Bool]):Vec[Vec[Bool]] = msk(VecInit(Seq.fill(1)(ptr)), VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B)))), false)
  /** 列仲裁指针扩展
   * @param ptr 指针值。支持2种格式
   *   - Vec[Bool]格式: 标准的温度计编码格式
   *   - Vec[Vec[Bool]]格式: 多优先级的温度计编码格式指针(每个优先级一个指针)
   * @param cmsk 列屏蔽值
   *   - 第一层Vec代表多个优先级屏蔽信息
   *   - 第二层Vec代表一个屏蔽bitmap列表。第i位表示对第i列是否需要屏蔽
   *   - 默认不屏蔽
   * @return 扩展后的仲裁器内部所需的cmsk信号格式。可以直接赋值给cmsk信号，例如：arb.io.cmsk := arb.cptr(...)
   * @note ptr和cmsk都支持RR格式下的扩展格式，用于一些特殊用途
   * @group Signal-Extends 
   */
  def cptr(ptr: => Vec[Bool], cmsk:Vec[Vec[Bool]]=VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B))))):Vec[Vec[Bool]] = 
    msk( VecInit(Seq.fill(1)(ptr)), cmsk, false )
  /** @group Signal-Extends */
  def rptr(ptr:Vec[Vec[Bool]]):Vec[Vec[Bool]] = msk(ptr, VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B)))), true)
  /** @group Signal-Extends */
  def rptr(ptr:Vec[Vec[Bool]], cmsk:Vec[Vec[Bool]]):Vec[Vec[Bool]] = msk( ptr, cmsk, true )
  /** @group Signal-Extends */
  def rptr(ptr: => Vec[Bool]):Vec[Vec[Bool]] = msk(VecInit(Seq.fill(1)(ptr)), VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B)))), true)
  /** 行仲裁指针扩展
   * @param ptr 指针值。支持2种格式
   *   - Vec[Bool]格式: 标准的温度计编码格式
   *   - Vec[Vec[Bool]]格式: 多优先级的温度计编码格式指针(每个优先级一个指针)
   * @param cmsk 列屏蔽值
   *   - 第一层Vec代表多个优先级屏蔽信息
   *   - 第二层Vec代表一个屏蔽bitmap列表。第i位表示对第i行是否需要屏蔽
   *   - 默认不屏蔽
   * @return 扩展后的仲裁器内部所需的rmsk信号格式。可以直接赋值给rmsk信号，例如：arb.io.rmsk := arb.rptr(...)
   * @note ptr和cmsk都支持RR格式下的扩展格式，用于一些特殊用途
   * @group Signal-Extends 
   */
  def rptr(ptr: => Vec[Bool], cmsk:Vec[Vec[Bool]]=VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(false.B))))):Vec[Vec[Bool]] = 
    msk( VecInit(Seq.fill(1)(ptr)), cmsk, true )


  /** 请求扩展：把原始请求`req`中不存在的位按rounbRobin方式扩充到npri*inrow*incol 
  * @param r 请求信息。支持4种格式
  *   - Bool格式：对所有优先级，所有行，所有列都提请求
  *   - Vec[Bool]格式: 根据isRowExp的选择而有所不同
  *     - isRowExp=true: 行请求，列扩展。请求r中的第i个元素被扩充到其所在行的所有列和所有优先级
  *     - isRowExp=false: 列请求，行扩展。请求r中的第i个元素被扩充到其所在列的所有列和所有优先级
  *   - Vec[Vec[Bool]]格式: 根据isMultiPri的选择而有所不同
  *     - isMultiPri=true: 第一层Vec代表优先级；第二层Vec代表对第0行的所有列的请求
  *     - isMultiPri=false: 第一层Vec代表行；第二层Vec代表对第0个优先级的所有列的请求
  *   - Vec[Vec[Vec[Bool]]]：bit[i][j][k]代表对优先级i，行j和列k的请求
  *   - true.B代表有请求；false.B代表无请求
  * @return 返回一个[[Arbiter]].io.r可以接收的请求格式
  * @group Signal-Extends 
  */
  def req( r:Vec[Vec[Vec[Bool]]] ) : Vec[Vec[Vec[Bool]]] = {
    VecInit( (0 until npri).map { case pp => 
      VecInit( (0 until inrow).map { case rr => 
        VecInit( (0 until incol).map { case cc => 
          val ppp = if( pp < r.size ) pp else pp%r.size
          val rrr = if( rr < r(ppp).size ) rr else rr%r(ppp).size
          val ccc = if( cc < r(ppp)(rrr).size ) cc else cc%r(ppp)(rrr).size
          r(ppp)(rrr)(ccc)
        } )
      } )
    } )
  }
  /** @group Signal-Extends */
  def req( r:Vec[Vec[Bool]], isMultiPri:Boolean = false ) : Vec[Vec[Vec[Bool]]] = {
    val ir = 
      if( isMultiPri ){
        r.map { case pr => VecInit(Seq.fill(1)(pr)) }
      }else{
        Seq.fill(1)(r)
      }
    req(VecInit(ir))
  }
  /** @group Signal-Extends */
  def req( r: => Vec[Bool] ) : Vec[Vec[Vec[Bool]]] = req( VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(r)))) )
  /** @group Signal-Extends */
  def req( r: => Vec[Bool], isRowExp:Boolean ) : Vec[Vec[Vec[Bool]]] = {
    if( !isRowExp ) req( VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(r)))) )
    else req( VecInit.tabulate(1){case i => VecInit(r.map(VecInit(_)))} )
  }
  /** @group Signal-Extends */
  def req( r: Bool ):Vec[Vec[Vec[Bool]]] = req( VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(VecInit(Seq.fill(1)(r)))))) )

  def gnt(isCol:Boolean=false) : Vec[Vec[Bool]] = if(isCol) io.gc(0) else io.gr(0)
  def gnt(r:Int) : Vec[Bool] = io.gr(0)(r)

  //====================================================================================================================
  //====                                      Information Show                                                      ====
  //====================================================================================================================

  protected def showMatrix( m:Vec[Vec[Bool]] ) = { m.map{ case v => p"    ${v}\n" }.reduce(_+_) }

  /** 仲裁详细信息
   *@note 一般用于定位问题，需要显示详细信息时使用
   *@example
   {{{
     test( new Module{
      val io = IO( new Bundle{
        val req = Input(UInt(4.W))
        val gnt = Output(Vec(4, UInt()))
      } )
      
      val arb = Arbiter.squeezeBubble( VecInit(io.req.asBools()) )
      io.gnt := VecInit(arb.io.gc(0).map( _.asUInt() ))

      printf(p"Current arbiter information is: \${arb.show()}")   // 调用chisel3.printf来显示仲裁器内部信息

    } ){ dut=>

      val result = Seq(
        "b0000".U -> Seq("b0000".U, "b0000".U, "b0000".U, "b0000".U),
        "b0001".U -> Seq("b0001".U, "b0000".U, "b0000".U, "b0000".U),
      )

      for( (r, g) <- result ){

        dut.io.req.poke(r)
        dut.clock.step()  // 注意：chisel3.printf仅在每次step时才会调用，所以必须在expect前先执行step操作
        g.zipWithIndex.foreach { case (gi,i) => dut.io.gnt(i).expect(gi) }

      }

    }
   }}}
   */
  def show(it:Int=0) = p"""
  ==== Arbiter Information [it=${it}] ====
  [cmsk]: ${io.cmsk(0)}
  [rmsk]: ${io.rmsk(0)}
  [reg]:
${showMatrix(io.r(0))}
  [gnt]:
${showMatrix(io.g(0))}
  [cmo]: ${io.cmo(0)}
  [rmo]: ${io.rmo(0)}
  """

}



/**常见仲裁算法封装
 *
 * 以下封装的所有算法本质都是[[SelectUnit]]的不同组织集成
 *
 * @group Encapuse
 * @groupdesc Scheduler 对常用场景的调度进行封装。该函数完成如下的操作：
 *    - 返回一个[[Arbiter]]调度Module的实例
 *    - 函数的输入信号将作为RoundRobin的输入信号接入该Module实例
 *    - RoundRobin的其它输入信号在函数内部使用默认的固定值接入Module实例
 */
object Arbiter{

  /** @group util */
  def reverse3( in:Vec[Vec[Vec[Bool]]], dim1:Boolean=false, dim2:Boolean=false, dim3:Boolean=false ):Vec[Vec[Vec[Bool]]] = {
    val res = VecInit(in.map { case preq =>
      val preq_res = VecInit(preq.map { case rreq => if(dim1) VecInit(rreq.reverse) else rreq })
      if(dim2) VecInit(preq_res.reverse) else preq_res
    })
    if(dim3) VecInit(res.reverse) else res
  }

  /** @group util */
  def reverse2( in:Vec[Vec[Bool]], dim1:Boolean=false, dim2:Boolean=false ):Vec[Vec[Bool]] = {
    val res = VecInit(in.map { case prmi => if(dim1) VecInit(prmi.reverse) else prmi })
    if(dim2) VecInit(res.reverse) else res
  }

  /** 按照Round-Robin规则调整in中各个元素的位置
   * @group util
   * @param in 输入信息集合
   * @param ptr Round Robin当前指针(指向最优先需要被选择的位置)
   * @param reverse 选择是否反向
   *   - 正向: 0->1->2->3
   *   - 反向：0->3->2->1
   * @return 调整后的新的in集合
   */
  def roundRobinD1[A<:Data]( in:Vec[A], ptr:UInt, reverse:Boolean=false ) = { xbB( in, rrSelect(in.size, reverse)(ptr) ) }

  /** 按照Round-Robin规则调整in(i)中各个元素的位置
   * @group util
   * @param in 输入信息集合
   * @param ptr Round Robin当前指针(指向最优先需要被选择的位置)(集合)
   *   - 第i个元素对应in(i)
   * @param reverse 选择是否反向
   *   - 正向: 0->1->2->3
   *   - 反向：0->3->2->1
   * @return 调整后的新的in集合
   */
  def roundRobinD2[A<:Data]( in:Vec[Vec[A]], ptr:Vec[UInt], reverse:Boolean ):Vec[Vec[A]] = { VecInit(in.zip(ptr).map { case (i,p) => roundRobinD1(i,p,reverse) }) }
  def roundRobinD2[A<:Data]( in:Vec[Vec[A]], ptr:Vec[UInt] ):Vec[Vec[A]] = { VecInit(in.zip(ptr).map { case (i,p) => roundRobinD1(i,p,false) }) }
  def roundRobinD2[A<:Data]( in:Vec[Vec[A]], ptr:UInt, reverse:Boolean ):Vec[Vec[A]] = VecInit(in.map { case i => roundRobinD1(i,ptr,reverse) })
  def roundRobinD2[A<:Data]( in:Vec[Vec[A]], ptr:UInt ):Vec[Vec[A]] = VecInit(in.map { case i => roundRobinD1(i,ptr,false) })

  /** 实例化一个Strict Priority调度器
   * @param r 输入请求信号
   *   - "1": 有效; 
   *   - 第i个位置表示第i个通道的请求
   * @param xoff 反压信号
   *   - true.B: 有反压;
   *   - false.B: 无反压
   * @return 返回一个RoundRobin模块(Module)的实例
   * @group Scheduler
   */
  def sp( r:Vec[Bool], xoff:Bool=false.B ) : Arbiter = {
    val ncol = r.length
    val arb = Module( new Arbiter( ncol ) )
    arb.io.rmsk := arb.rmsk( xoff )
    arb.io.cmsk := arb.cmsk( false.B )
    arb.io.r := arb.req( r )
    arb
  }

  /** 实例化一个Round Robin调度器
   * @param r 调度器请求。bit[i]对应请求序号i; true.U代表有请求，false.U代表无请求
   * @param p 选择指针。
   * @return 调度器Module实例
   * @group Scheduler
   */
  def rr( r:Vec[Bool], p:Vec[Bool] ) : Arbiter = {
    val ncol = r.length
    val arb = Module( new Arbiter( ncol, colMode=1 ) )
    arb.io.rmsk := arb.rmsk( false.B )
    arb.io.cmsk := arb.cmsk( false.B )
    arb.io.r := arb.req( r )
    arb
  }

  /** 实例化一个Round-Robin策略的Until "0"调度器
   * @param r 调度器请求。bit[i]对应请求序号i; true.U代表有请求，false.U代表无请求
   * @return 调度器Module实例
   * @group Scheduler
   */
  def rrUntil0( r:Vec[Bool] ) : Arbiter = {
    val arb = Module( new Arbiter( r.size, colMode=2, selMode=3 ) )
    arb.io.rmsk := arb.rmsk( false.B )
    arb.io.cmsk := arb.cmsk( false.B )
    arb.io.r := arb.req( r )
    arb
  }

  /** Onehot控制的MUX选择
   * @param in 被选择的数据数据列表
   * @param cgnt Onehot编码的选择信号. Note: 若cgnt==0，则该函数输出数据也为"0"
   * @return 选择的数据
   * @group XB
   */
  def muxO[B<:Data](in:Vec[B], cgnt:Vec[Bool]):B = {
    val ins = in.map(_.asUInt())
    val s = ins(0).getWidth
    cgnt
      .map {case b => VecInit(Seq.fill(s)(b))}
      .map {case b => b.asUInt()}
      .zip (ins)
      .map {case (s,c) => s&c}
      .reduce (_|_)
      .asTypeOf( in(0) )
  }

  /** Binary控制的MUX选择
   * @param in 被选择的数据数据列表
   * @param sel binary编码的选择信号. 
   * @return 选择的数据
   * @group XB
   */
  def muxB[B<:Data](in:Vec[B], sel:UInt):B = in(sel)

  /** Onehot格式控制的Crossbar
   * @param in 被选择的数据列表
   * @param sels Onehot编码的选择信号列表
   *  - 若sels(i)==0，则该函数输出数据也为"0"
   *  - 第i个选择信号对应第i个输出
   * @return 选择的sels.size个输出
   * @note 一般该函数的选择信号`sels`从[[Arbiter]]仲裁器的输出获得
   * @group XB
   * @example 4个输入数据__inData:Vec(4, UInt(128.W))__，8个Onehot编码的输出选择信号:__sels:Vec(8, Vec(4, Bool()))__，该Crossbar的输出__outData__可以写为：
   {{{
     val outData = xbO(inData, sels)
   }}}
   */
  def xbO[B<:Data](in:Vec[B], sels:Vec[Vec[Bool]], inFF:Boolean=false, selFF:Boolean=false, outFF:Boolean=false):Vec[B] = {
    val in_inner = if( inFF ) RegNext(in) else in
    val sels_inner = if( selFF ) RegNext(sels) else sels
    val res = VecInit(sels_inner.map {case s=>muxO(in_inner, s)})
    if( outFF ) RegNext(res) else res
  }

  /** Binary格式控制的Crossbar
   * @param in 被选择的数据列表
   * @param sels Binary编码的选择信号列表
   * @return 选择的sels.size个输出
   * @group XB
   */
  def xbB[B<:Data](in:Vec[B], sels:Seq[UInt]):Vec[B] = VecInit( sels.map {case s => muxB(in, s)} )

  /** RoundRobin选择的下一跳
   * @param omsk 当前选择的cmo/rmo输出
   * @group Pointer
   */
  def nextPtr( omsk:Vec[Bool] ):Vec[Bool] = {
    val s = (omsk.size+1) / 2
    Mux( omsk(s-1), VecInit(omsk.slice(s, omsk.size)), VecInit(omsk.slice(0, s) ))
  } 

  /** 实例化一个挤气泡仲裁器
   * @param r 输入数据有效标识. 该输入将作为请求信号链接到仲裁器
   *   - "1": 有效; 
   *   - 第i个位置表示第i个通道的输入数据
   * @param oNum 输出通道个数
   *   - oNum > 0: 有效的输出通道个数
   *   - oNum <= 0: 输出通道个数和输入通道个数保持一致
   * @return 返回一个RoundRobin模块(Module)的实例
   * @group Scheduler
   *
   * @example 4通道输入数据挤气泡仲裁: 输入数据按先后顺序从4个通道进入，同一个clock来的数据，通道序号小的数据靠前；
   * 输出为8个通道，数据按照先后顺序在8个输出通道间RoundRobin排布(从0号输出通道开始)
   * {{{
      ......
      val req = VecInit( inPkt.map(_.vld) )  // 拿各个输入通道数据的有效标识作为仲裁器的请求。`inPkt`为4个输入通道数据(Vec[Pkt]类型)
      val arb = Arbiter.squeezeBubble( req, 8 )   // 调用函数，设置输入请求和输出通道个数
      val gnt = arb.io.gnt    // 获取仲裁结果列表。该列表是一个Vec[Vec[Bool]]类型。其中位置(i,j)=true.B表示第i个输出通道需要从第j个输入通道选择数据
      val odata = xbO(inPkt, gnt)   // 根据仲裁结果配置Crossbar，获取8个输出通道的数据(Vec[Pkt]类型)
      ......
   }}}
   */
  def squeezeBubble( r:Vec[Bool], oNum:Int=0 ) : Arbiter = {
    val ncol = if( oNum < 1 ) r.size else oNum
    val arb = Module(new Arbiter( r.size, ncol, colMode = 2 ))
    arb.io.rmsk := arb.rmsk( false.B )
    arb.io.cmsk := arb.cmsk( false.B )
    arb.io.r := arb.req( r, true )
    arb
  }

  /** 时序优化挤气泡仲裁器
   *
   * __根据挤气泡电路的特点，把RR拆分为SP仲裁和Grant的XB组合，其中XB组合靠pointer来选择__
   * @return 返回每个输出口的交叉配置
   * @group Scheduler
   */
  def squeezeBubble2( r:Vec[Bool], oNum:Int=0, xoff:Bool=false.B ) : Vec[Vec[Bool]] = {
    val nrow = r.size 
    val ncol = oNum 
    val s = log2Up(oNum+1)
    
    // Arbiter Implement
    val arb = Module( new Arbiter( ncol, nrow ) )
    arb.io.rmsk := arb.rmsk( false.B )
    arb.io.cmsk := arb.cmsk( false.B )
    arb.io.r := arb.req( r, true )

    val squeezeBubblePtr = RegInit( 0.U(log2Up(oNum).W) )

    val cfgs = rrSelect(oNum)    // 获取Round-Robin选择表内容
    val cfg = cfgs(squeezeBubblePtr)  // 根据当前指针进行选择

    val res = cfg.map { case c => arb.io.gc(0)(c) }   // Crossbar交叉：使用配置选择信息从gc中选择对应的Grant

    // Update Pointer
    val num = r.map{ case rr => Mux(rr, 1.U(s.W), 0.U(s.W)) }.reduce(_+_)
    when( !xoff ){
      val su = squeezeBubblePtr + num
      squeezeBubblePtr := Mux(su>=oNum.U, su-oNum.U, su)
    }

    VecInit(res)
  }

  /** 生成RoundRobin选择算法下的Grant选择表
   *
   * 该配置选择表为一个二维数组矩阵表
   *   - 第一个维度为`nodeNum`个元素的Vec。用于电路使用Round Robin指针来选择
   *   - 第二个维度为`nodeNum`个元素的Vec。用于指示每个输出口的选择
   *   - 每个元素为一个UInt类型。用于进行Mux选择
   *
   * @param nodeNum Round-Robin节点个数
   * @param reverse 是否反向
   *   - 对于Grant选择，一般为反向
   *   - 对于Request选择，一般为正向
   * @return 返回Grant选择表
   *
   * @example 对于4个节点的RoundRobin配置表，反向输出的内容为:
   {{{
     VecInit(
       VecInit( 0.U, 1.U, 2.U, 3.U ),
       VecInit( 3.U, 0.U, 1.U, 2.U ),
       VecInit( 2.U, 3.U, 0.U, 1.U ),
       VecInit( 1.U, 2.U, 3.U, 0.U ),
     )
   }}}
   正向输出的内容为
   {{{
     VecInit(
       VecInit( 0.U, 1.U, 2.U, 3.U ),
       VecInit( 1.U, 2.U, 3.U, 0.U ),
       VecInit( 2.U, 3.U, 0.U, 1.U ),
       VecInit( 3.U, 0.U, 1.U, 2.U ),
     )
   }}}
   * @group util
   */
  def rrSelect( nodeNum:Int, reverse:Boolean=false ) : Vec[Vec[UInt]] = {
    val res = (0 until nodeNum).map { case s =>
      val r = (0 until nodeNum)
        .map{ case o => 
            if(reverse)  (o - s + nodeNum)%nodeNum 
            else (o+s)%nodeNum
          }
        .map( _.U )
      VecInit(r)
    }
    VecInit(res)
  }

  /** 维度提升
   * @group util
   */
  def lift( in:Vec[Vec[Bits]] ) : Vec[Vec[Bits]] = VecInit.tabulate(in.size){ case x =>
    VecInit.tabulate(in(0)(0).getWidth){ case z =>
      Cat( for( y <- in(0).size-1 to 0 by -1 ) yield in(x)(y)(z) )
    }
  }

  /** 维度提升
   * @group util
   */
  def unlift( in:Vec[Vec[Bits]] ) : Vec[Vec[Bits]] = VecInit.tabulate(in(0)(0).getWidth){ case z =>
    VecInit.tabulate(in(0)(0).getWidth){ case y =>
      Cat( for( x <- in.size-1 to 0 by -1 ) yield in(x)(y)(z) )
    }
  }

}


// /** RoundRobin module IO
//   */
// class RoundRobinIO(reqNum:Int, priNum:Int) extends Bundle{

//   val SZ_TYPE = 1
//   /** schedule request bitmap */
//   val req = Input(UInt(reqNum.W))
//   /** schedule request pointer (binary format) */
//   val ptr = Input(UInt(log2Up(reqNum).W))
//   /** Selection bitmap for each priority */
//   val sels = Input(Vec(priNum, UInt(reqNum.W)))
//   /** Selection type for each priority
//     *     - 0: Strict-Priority Scheduling
//     *     - 1: Round-Robin Scheduling
//     */
//   val schTypes = Input(Vec(priNum, UInt(SZ_TYPE.W)))

//   /** schedule grant (binary format) */
//   val gnt = Output(UInt(log2Up(reqNum).W))
//   /** Grant valid flag */
//   val gnt_vld = Output(Bool())
//   /** Grant (One-Hot foramt) */
//   val gnt_bmp = Output(UInt(reqNum.W))
//   /** Reversed grant (binary foramt) */
//   val gntr = Output(UInt(log2Up(reqNum).W))
//   /** Reversed grant valid flag */
//   val gntr_vld = Output(Bool())
//   /** Reversed grant (One-Hot foramt) */
//   val gntr_bmp = Output(UInt(reqNum.W))

//   override def cloneType: RoundRobinIO.this.type = new RoundRobinIO(reqNum, priNum).asInstanceOf[this.type]
// }

// /** roundRobin scheduler algorithm
//   *
//   * @param reqNum maximum request number
//   * @param priNum 优先级个数
//   * @param isInnerPtr Whether has inner pointer register
//   *
//   * @note SPL and SP level can NOT support Reversed-Grant. Because we can not select consecutive TWO request in one cycle
//   *
//   * =Application=
//   *   - Typical Strict-Priority: level="b00100" and io.ptr = 0
//   *   - Typical Round-Robin: level="b00100"
//   *     - level2: RR
//   *   - WRR: level="b01100"
//   *     - level1: WRR low
//   *     - level2: WRR high
//   *   - SP+WRR+SPL: level="b11110"
//   *     - level0: SPL
//   *     - level1: WRR low
//   *     - level2: WRR high
//   *     - level3: SP
//   *   - CIR+EIR: level="b11111"
//   *     - level0: EIR SPL
//   *     -  level1: EIR WRR low
//   *     - level2: EIR WRR high
//   *     - level3: EIR SP
//   *     - level4: CIR
//   * */
// class RoundRobinImpl(
//                 reqNum : Int = 4,
//                 priNum : Int = 1,
//                 isInnerPtr : Boolean = true,
//                 ) extends Module {

//   // IO definition
//   val io = IO(new RoundRobinIO(reqNum, priNum))

//   def findLast(req:UInt, isReverse:Bool=true.B) = {
//     val wid = log2Up(req.getWidth)
//     val ret = Wire(UInt(wid.W))
//     val rs = req.toBools()

//     ret := 0.U

//     when( isReverse === true.B ){
//       for( i <- 0 until req.getWidth by 1 ){
//         when( rs(i) === true.B ){
//           ret := i.U(wid.W)
//         }
//       }
//     }.otherwise{
//       for( i <- req.getWidth-1 to 0 by -1 ){
//         when( rs(i) === true.B ){
//           ret := i.U(wid.W)
//         }
//       }
//     }

//     ret
//   }

//   def findRoundLast(req:UInt, ptr:UInt, isReverse:Bool=true.B) = {
    
//     val wid = log2Up(req.getWidth)
//     val sel_bmp = (-1.S(wid.W).asUInt() << ptr).asUInt()
//     val sel_high = sel_bmp & req
//     val sel_low =  req & (~sel_bmp).asUInt()

//     Mux(sel_high.orR, findLast(sel_high, isReverse),
//       findLast(sel_low, isReverse))
//   }

//   val inner_ptr = if( isInnerPtr ) RegInit(0.U(log2Up(reqNum).W)) else io.ptr

//   val ptrr = Mux(inner_ptr===(reqNum-1).U, 0.U, inner_ptr-1.U)
//   io.gnt := 0.U
//   io.gntr := 0.U

//   for( (sel, schType) <- io.sels.zip(io.schTypes) ){
//     val sel_req = io.req & sel
//     when( sel_req.orR ){
//       if( schType == 0 ){
//         io.gnt := findLast( sel_req, false.B )
//         io.gntr := findLast( sel_req, true.B )
//       }else{
//         io.gnt := findRoundLast( sel_req, inner_ptr )
//         io.gntr := findRoundLast( sel_req, ptrr, true.B )
//       }
//     }
//   }

//   if( isInnerPtr ){
//     when( io.gnt_vld ){
//       when( inner_ptr === reqNum.U - 1.U ){
//         inner_ptr := 0.U
//       }.otherwise{
//         inner_ptr := inner_ptr + 1.U
//       }
//     }
//   }

//   io.gnt_vld := io.req.orR
//   io.gntr_vld := io.gnt=/=io.gntr
//   io.gnt_bmp := Mux( io.gnt_vld, UIntToOH(io.gnt), 0.U )
//   io.gntr_bmp := Mux( io.gntr_vld, UIntToOH(io.gntr), 0.U )

// }

// /** Round-Robin scheduling algorithm encapsulation class
//   *
//   * == Strict-Priority ==
//   * Strict-Priority is a ''find'' search algorithm which search first "1" from request signal and the search begin
//   * position is 0(for gnt) and -1(for gntr)
//   *
//   * == Round-Robin ==
//   * Round-Robin is a ''find'' search algorithm which search first "1" from request signal and the search begin
//   * position is ptr(for gnt) and ptr-1(for gntr). When there is no "1" to find, Round-Robin will re-search from direction
//   * orignal position.
//   *
//   *   - For gnt, Round-Robin search "1" from ptr to reqNum firstly. If there is no "1" to find, Round-Robin will re-search
//   * "1" from 0 to ptr-1 secondly. If there is no "1" again, Round-Round return invalid grant.
//   *   - For gntr, Round-Robin search "1" from ptr-1 to 0 firstly. If there is no "1" to find, Round-Robin will re-search
//   * "1" from reqNum-1 to ptr secondly. If there is no "1" again, Round-Round return invalid grant.
//   *
//   * @note `RoundRobin` embed a [[RoundRobinImpl]] module to provide base algorithm logic circuit.
//   *       User can directly instant `RoundRobinImpl` module and connect corresponse IO [[RoundRobinIO]]
//   *
//   * @constructor Create a `RoundRobin` instance who enbed a `RoundRobinImpl` logic
//   * @param reqNum Request node number
//   * @param priNum Selection priority number
//   * @param hasInnerPtr Include inner pointer register enable flag
//   *
//   * @example Basic Round-Robin from 4 request node
//   *          {{{
//   *          val req = WireInit("b1010".U)    // Request
//   *          val ptr = WireInit(2.U)          // Pointer for Round-Robin
//   *
//   *          val rr = RoundRobin(4)     // Basic Round-Robin
//   *          val gnt_rr = rr.roundRobin( req, ptr )
//   *
//   *          val sp = RoundRobin(4)     // Basic Strict-Priority
//   *          val gnt_sp = sp.roundRobin( req )
//   *          }}}
//   *
//   * @example Two level Round-Robin SP scheduling
//   *          {{{
//   *             val req = WireInit("b1010".U)    // Request
//   *             val ptr = WireInit(2.U)          // Pointer for Round-Robin
//   *
//   *             // Priority(0) is RR and Priority(1) is also RR. So schTypes=[1,1]
//   *             val rr = RoundRobin(4, 2).setSelect( "b1100".U, 0).setSelect("b0011", 1)
//   *             val gnt = rr.roundRobin( req, ptr )
//   *          }}}
//   */
// class RoundRobin(
//                 reqNum : Int,
//                 priNum : Int = 1,
//                 hasInnerPtr : Boolean = true,
//                 ) {
//   protected val rr = Module(new RoundRobinImpl(reqNum, priNum, hasInnerPtr))
//   protected val sels = WireInit(VecInit(Seq.tabulate(priNum)(x=>0.U(reqNum.W))))
//   protected val isBigPriority = WireInit(0.B)

//   /** Set(Connect) priority request selection signals
//     *
//     * @param sel Select signal
//     * @param pri Select priority
//     * @return This `RoundRobin` instance
//     */
//   def setSelect( sel:UInt=0.U(reqNum.W), pri:Int=0) = {
//     sels(pri) := sel
//     this
//   }

//   /** Round-Robin select one from `req` starting with position `ptr`
//    * @param req Selection request. Bit[i] represent request[i], and Bit[i]=1 means a valid request
//    * @param ptr Selection staring position. The direction of selection is from LSB to MSB
//    * @return A tuple which format is (valid, grant)
//     *
//     * @note This is signal connection function
//    */
//   def roundRobin( req:UInt, ptr:UInt = 0.U ):(Bool,UInt) = {
//     rr.io.req := req
//     rr.io.ptr := ptr

//     ( rr.io.gnt_vld, rr.io.gnt )
//   }

//   /** Same process with `roundRobin` except `req` parameters */
// //   def roundRobin( req:Vec[Bool], ptr:UInt = 0.U ):(Bool,UInt) = {
// //     roundRobin( req.asUInt(), ptr )
// //   }

//   /** Round-Robin select one from `req` starting with position `ptr`
//    * @param req Selection request. Bit[i] represent request[i], and Bit[i]=1 means a valid request
//    * @param ptr Selection staring position. The direction of selection is from LSB to MSB
//    * @return A bitmap format grant which include a valid grant position information
//    */
//   def roundRobinOnehot( req:UInt, ptr:UInt = 0.U ):UInt = {
//     rr.io.req := req
//     rr.io.ptr := ptr

//     rr.io.gnt_bmp
//   }

//   /** Same process with `roundRobinOnehot` except `req` parameters */
// //   def roundRobinOnehot( req:Vec[Bool], ptr:UInt = 0.U ):UInt = {
// //     roundRobinOnehot( req.asUInt(), ptr )
// //   }

//   /** Round-Robin select one from `req` starting with position `ptr`
//    * @param req Selection request. Bit[i] represent request[i], and Bit[i]=1 means a valid request
//    * @param ptr Selection staring position. The direction of selection is from LSB to MSB
//    * @return A tuple which format is (left_grant_valid, left_grant, right_grant_valid, right_grant)
//    */
//   def roundRobinDual( req:UInt, ptr:UInt = 0.U ):(Bool, UInt, Bool, UInt) = {
//     rr.io.req := req
//     rr.io.ptr := ptr

//     ( rr.io.gnt_vld, rr.io.gnt, rr.io.gntr_vld, rr.io.gntr )
//   }

//   /** Same process with `roundRobinDual` except `req` parameters */
// //   def roundRobinDual( req:Vec[Bool], ptr:UInt = 0.U ):(Bool, UInt, Bool, UInt) = {
// //     roundRobinDual( req.asUInt(), ptr )
// //   }

//   /** Round-Robin select one from `req` starting with position `ptr`
//    * @param req Selection request. Bit[i] represent request[i], and Bit[i]=1 means a valid request
//    * @param ptr Selection staring position. The direction of selection is from LSB to MSB
//    * @return A bitmap format grant which include up to TWO grant position information
//    */
//   def roundRobinBitmap( req:UInt, ptr:UInt = 0.U ):UInt = {
//     rr.io.req := req
//     rr.io.ptr := ptr

//     rr.io.gnt_bmp | rr.io.gntr_bmp
//   }

//   /** Same process with `roundRobinBitmap` except `req` parameters */
// //   def roundRobinBitmap( req:Vec[Bool], ptr:UInt = 0.U ): UInt = {
// //     roundRobinBitmap( req.asUInt(), ptr )
// //   }

//   rr.io.sels := sels
// }

// import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation}

// object RoundRobinExample extends App {

//   override def main(args:Array[String]):Unit = {
//     (new chisel3.stage.ChiselStage).execute(
//       Array("-X", "verilog"),
//       Seq( ChiselGeneratorAnnotation( () => new Module{
//         val io = IO(new Bundle{
//           val r = Input(UInt(8.W))
//           val p = Input(UInt(8.W))
//           val g = Output(UInt(8.W))
//         })
//         val rr = Arbiter.rr( io.r, io.p )
//         io.g := rr.io.g(0)(0)
//       } ) )
//     )
//   }

// }
